package com.sunrise.generico.acesso;

import java.util.List;

import javax.annotation.Resource;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Transactional;

import com.sunrise.generico.entidade.iface.EntidadeNegocio;
import com.sunrise.generico.repositorio.iface.RepositorioNegocio;

public abstract class DaoNegocioService<E extends EntidadeNegocio> extends HibernateDaoSupport implements RepositorioNegocio<E>{
	

	/**
	 * Altera uma entidade
	 * 
	 * @param <E>
	 *            Tipo da entidade
	 * @param entity
	 *            Entidade a ser alterada
	 */
	@Transactional(readOnly = false)
	@Override
	public void alterar(E entity) {
		getHibernateTemplate().update(entity);
	}

	/**
	 * Exclui uma entidade
	 * 
	 * @param <E>
	 *            Tipo da entidade
	 * @param entity
	 *            Entidade a ser exclu�da
	 */
	@Transactional(readOnly = false)
	@Override
	public void remover(E entity) {
		getHibernateTemplate().delete(entity);
	}

	/**
	 * Insere uma entidade
	 * 
	 * @param <E>
	 *            tipo da entidade
	 * @param entity
	 *            Entidade a ser inserida
	 * @return ID da entidade inserida
	 */
	@Transactional(readOnly = false)
	@Override
	public void inserir(E entity) {
		getHibernateTemplate().save(entity);
	}
	
	/**
	 * Configura o SessionFactory do hibernate.
	 * 
	 * @param sessionFactory
	 *            SessionFactory do hibernate
	 */
	@Resource(name = "sessionFactory")
	protected void setSessionFactory2(SessionFactory sessionFactory) {
		setSessionFactory(sessionFactory);
	}

	/**
	 * Lista as entidades cadastradas
	 * 
	 * @param <E>
	 *            Tipo da entidade
	 * @param clazz
	 *            Tipo da entidade
	 * @return Lista de entidades
	 */
	@SuppressWarnings({ "unchecked", "hiding" })
	@Transactional(readOnly = true)
	public <E extends EntidadeNegocio> List<E> listar(Class<E> clazz) {
		Query query = createQuery("from " + clazz.getSimpleName());
		List<E> list = query.list();
		return list;
	}

	/**
	 * Retorna uma lista de entidades que atendam � query especificada. Lan�a
	 * uma exce��o caso a query n�o possa ser executada.
	 * 
	 * @param query
	 *            Query a ser executada
	 * @param args
	 *            Par�metros a serem colocados na query. Pode ser null caso os
	 *            par�metros j� estejam contidos. Devem estar na mesma ordem em
	 *            que foram definidos na query.
	 * @return Lista de entidades
	 */
	@SuppressWarnings({ "rawtypes" })
	public List executeQuery(String query, Object... args) {
		Query queryObj = createQuery(query, args);
		List list = queryObj.list();
		return list;
	}

	/**
	 * Execute a hql
	 * 
	 * @param query
	 * @return
	 */
	public Object executeQuerySingleResult(String query, Object... args) {
		Query obj = createQuery(query, args);
		obj.setMaxResults(1);
		return obj.uniqueResult();
	}

	/**
	 * Retorna um objeto Query relativo � consulta passada como par�metro
	 * 
	 * @param query
	 *            query a ser gerada
	 * @param args
	 *            Par�metros a serem colocados na query. Pode ser null caso os
	 *            par�metros j� estejam contidos. Devem estar na mesma ordem em
	 *            que foram definidos na query.
	 * @return Objeto query
	 */
	@SuppressWarnings("hiding")
	private <E extends EntidadeNegocio> Query createQuery(String query, Object... args) {
		Query obj = getSession().createQuery(query);
		if (args != null) {
			int index = 0;
			for (Object arg : args) {
				obj.setParameter(index, arg);
				index++;
			}
		}
		return obj;
	}

	/**
	 * Obt�m uma entidade a partir de um exemplo
	 * 
	 * @param <E>
	 *            Tipo da entidade
	 * @param clazz
	 *            Tipo da entidade
	 * @param example
	 *            Exemplo a ser seguido
	 * @return Lista de entidades
	 */
	@SuppressWarnings({ "unchecked", "hiding" })
	@Transactional(readOnly = true)
	public <E extends EntidadeNegocio> List<E> searchByExample(Class<E> clazz,
			E example) {
		Criteria crit = getSession().createCriteria(clazz);
		Example sample = Example.create(example);
		sample.enableLike(MatchMode.START);
		sample.ignoreCase();
		sample.excludeZeroes();
		crit.add(sample);
		List<E> list = crit.list();
		return list;
	}

	/**
	 * Retorna uma entidade pelo seu ID
	 * 
	 * @param <E>
	 *            Tipo da entidade
	 * @param id
	 *            ID da entidade
	 * @param clazz
	 *            Tipo da entidade
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "hiding" })
	@Transactional(readOnly = true)
	public <E extends EntidadeNegocio> E getById(Long id, Class<E> clazz) {
		E entity = (E) getSession().get(clazz, id);
		return entity;
	}
	
}
